﻿using System;
using System.ComponentModel;
using System.Windows;

namespace Notoric.ViewModel.Elements.CWNotation
{
    /// <summary>
    /// Adapts a <see cref="Model.Elements.CWNotation.Brace"/> instance to a Windows Presentation Foundation View.
    /// </summary>
    /// <remarks>
    /// The brace is made from two symetric halves. This class defines the points for the upper half,
    /// the other one can be easily created in the view using axial symmetry.<br/>
    /// The symbol consists of two cubic Bézier curves that share the <see cref="StartPoint"/>
    /// and the <see cref="CentralPoint"/>. The space in-between should be filled.
    /// </remarks>
    public class Brace : StaffConnector
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="Brace"/> instance.
        /// </summary>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="System"/>
        /// </para>
        /// <para>
        /// The element that directly owns this element.
        /// </para>
        /// </param>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="Model.Elements.CWNotation.Brace"/>
        /// </para>
        /// <para>
        /// The adapted model element instance.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="adaptedElement"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="NullReferenceException">
        /// <paramref name="owner"/> is <see langword="null"/>.
        /// The values of the <see cref="ViewModelElement.UndoStack"/> and <see cref="ViewModelElement.RedoStack"/> properties
        /// are passed directly to the base class (<see cref="ViewModelElement"/>) constructor. Thus, there is not
        /// an occasion to react with a more appropriate exception.
        /// </exception>
        public Brace(System owner, Model.Elements.CWNotation.Brace adaptedElement)
            : base(owner, adaptedElement)
        {
            SetPoints();
            this.PropertyChanged += new PropertyChangedEventHandler(Brace_PropertyChanged);
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return elementNameKey; }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when a property of this object has changed.
        /// </summary>
        /// <remarks>
        /// <b>Operation:</b><br/>
        /// If the modified property is <see cref="StaffConnector.Top"/> or <see cref="StaffConnector.Center"/>
        /// the handler calls the <see cref="SetPoints"/> method.
        /// </remarks>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Brace_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Top" || e.PropertyName == "Center" || e.PropertyName == "Bottom")
                SetPoints();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Computes the position of control points.
        /// </summary>
        private void SetPoints()
        {
            var system = FindOwner<System>();
            var x = system.Left - 2;
            var ctrlPtDist = (Center - Top) / 4;

            StartPoint = new Point(x, Top);
            CentralPoint = new Point(x - 8, Center);
            UpperControlPoint1 = new Point(x - 20, Top + ctrlPtDist);
            UpperControlPoint2 = new Point(x - 19, Top + ctrlPtDist);
            LowerControlPoint1 = new Point(x + 7, Center - ctrlPtDist);
            LowerControlPoint2 = new Point(x + 9, Center - ctrlPtDist);

            RaisePropertyChanged("StartPoint");
            RaisePropertyChanged("CentralPoint");
            RaisePropertyChanged("UpperControlPoint1");
            RaisePropertyChanged("UpperControlPoint2");
            RaisePropertyChanged("LowerControlPoint1");
            RaisePropertyChanged("LowerControlPoint2");
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the coordinates of the brace starting point.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the brace
        /// starting point relative to the top-left corner of the owner section.
        /// </para>
        /// </value>
        public Point StartPoint
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the coordinates of the brace central point.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the brace
        /// central point relative to the top-left corner of the owner section.
        /// </para>
        /// </value>
        public Point CentralPoint
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the coordinates of the upper control point for the first Bézier curve.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the upper control point 
        /// of the first (outer) Bézier curve relative to the top-left corner 
        /// of the owner section.
        /// </para>
        /// </value>
        public Point UpperControlPoint1
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the coordinates of the upper control point for the second Bézier curve.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the upper control point 
        /// of the second (inner) Bézier curve relative to the top-left corner 
        /// of the owner section.
        /// </para>
        /// </value>
        public Point UpperControlPoint2
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the coordinates of the lower control point for the first Bézier curve.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the lower control point 
        /// of the first (outer) Bézier curve relative to the top-left corner 
        /// of the owner section.
        /// </para>
        /// </value>
        public Point LowerControlPoint1
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the coordinates of the lower control point for the second Bézier curve.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Point"/>
        /// </para>
        /// <para>
        /// The coordinates (in device independent pixels) of the lower control point 
        /// of the second (inner) Bézier curve relative to the top-left corner 
        /// of the owner section.
        /// </para>
        /// </value>
        public Point LowerControlPoint2
        {
            get;
            private set;
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the dictionary key of a user-friendly name of this element.
        /// </summary>
        private const string elementNameKey = "ViewModel.ElementNames.Accidental";

        #endregion
    }
}
